home *** CD-ROM | disk | FTP | other *** search
/ Delphi Anthology / aDELPHI.iso / Runimage / Delphi50 / Source / Property Editors / stfilsys.pas < prev    next >
Pascal/Delphi Source File  |  1999-08-11  |  15KB  |  519 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Delphi Visual Component Library                 }
  5. {       TStrings virtual file system                    }
  6. {                                                       }
  7. {       Copyright (c) 1999 Borland International        }
  8. {                                                       }
  9. {*******************************************************}
  10.  
  11. unit StFilSys;
  12.  
  13. interface
  14.  
  15. uses Windows, ActiveX, SysUtils, Classes, IStreams, TypInfo, ToolsAPI;
  16.  
  17. const
  18.   sTStringsFileSystem = 'Delphi.TStringsFileSystem';
  19.  
  20. type
  21.   TActiveTStringsProperty = class;
  22.  
  23.   { TOTANotifier }
  24.  
  25.   TOTANotifier = class(TInterfacedObject)
  26.   protected
  27.     FOwner: TActiveTStringsProperty;
  28.     { IOTANotifier }
  29.     procedure AfterSave;
  30.     procedure BeforeSave;
  31.     procedure Destroyed;
  32.     procedure Modified;
  33.   public
  34.     constructor Create(AOwner: TActiveTStringsProperty);
  35.   end;
  36.  
  37.   { TOTAModuleNotifier }
  38.  
  39.   TOTAModuleNotifier = class(TOTANotifier, IOTANotifier, IOTAModuleNotifier)
  40.   protected
  41.     { IOTANotifier }
  42.     procedure Modified;
  43.     { IOTAModuleNotifier }
  44.     function CheckOverwrite: Boolean;
  45.     procedure ModuleRenamed(const NewName: string);
  46.   end;
  47.  
  48.   { TOTAFormNotifier }
  49.  
  50.   TOTAFormNotifier = class(TOTANotifier, IOTANotifier, IOTAFormNotifier)
  51.   protected
  52.     { IOTAFormNotifier }
  53.     procedure FormActivated;
  54.     procedure FormSaving;
  55.     procedure ComponentRenamed(ComponentHandle: TOTAHandle;
  56.       const OldName, NewName: string);
  57.   end;
  58.  
  59.   { TActiveTStringsProperty }
  60.  
  61.   TActiveTStringsProperty = class(TComponent)
  62.   private
  63.     FComponent: TComponent;
  64.     FPropertyName: string;
  65.     FModule: IOTAModule;
  66.     FFormEditor: IOTAFormEditor;
  67.     FModuleNotifier: IOTAModuleNotifier;
  68.     FFormModuleNotifier: IOTAFormNotifier;
  69.     FIndex: Integer;
  70.     FModuleNotifierIndex, FFormNotifierIndex: Integer;
  71.     FDiskAge: Longint;
  72.     procedure SetModule(const Value: IOTAModule);
  73.     procedure SetComponent(Value: TComponent);
  74.   protected
  75.     procedure Notification(AComponent: TComponent;
  76.       Operation: TOperation); override;
  77.   public
  78.     constructor CreateNew(AOwner, AComponent: TComponent;
  79.       const APropertyName: string);
  80.     destructor Destroy; override;
  81.     procedure RenameComponent(const OldName, NewName: string);
  82.     property Module: IOTAModule read FModule write SetModule;
  83.     property Component: TComponent read FComponent write SetComponent;
  84.     property PropertyName: string read FPropertyName write FPropertyName;
  85.     property DiskAge: Longint read FDiskAge write FDiskAge;
  86.   end;
  87.  
  88.   { TStringsStream }
  89.  
  90.   TStringsStream = class(TIMemoryStream)
  91.   private
  92.     FComponent: TComponent;
  93.     FStringsProperty: TStrings;
  94.     FActiveTStringsProperty: TActiveTStringsProperty;
  95.     FModified: Boolean;
  96.     constructor Create(ActiveTStringsProperty: TActiveTStringsProperty;
  97.       AComponent: TComponent; const APropName: string; Mode: Integer);
  98.     procedure SetDesignerModified;
  99.   public
  100.     destructor Destroy; override;
  101.     function Write(pv: Pointer; cb: Longint; pcbWritten: PLongint): HResult; override;
  102.   end;
  103.  
  104.   { TStringsFileSystem }
  105.  
  106.   TStringsFileSystem = class(TInterfacedObject, IOTAFileSystem)
  107.   private
  108.     FActiveTStringsProperties: TStringList;
  109.   public
  110.     constructor Create;
  111.     destructor Destroy; override;
  112.     function GetTStringsProperty(const IDent: string;
  113.       AComponent: TComponent; const APropertyName: string): TActiveTStringsProperty;
  114.     function GetFileStream(const FileName: string; Mode: Integer): IStream;
  115.     function FileAge(const FileName: string): Longint;
  116.     function RenameFile(const OldName, NewName: string): Boolean;
  117.     function IsReadonly(const FileName: string): Boolean;
  118.     function IsFileBased: Boolean;
  119.     function DeleteFile(const FileName: string): Boolean;
  120.     function FileExists(const FileName: string): Boolean;
  121.     function GetTempFileName(const FileName: string): string;
  122.     function GetBackupFileName(const FileName: string): string;
  123.     function GetIDString: string;
  124.   end;
  125.  
  126. var
  127.   StringsFileSystem: TStringsFileSystem = nil;
  128.   StringsFileSystemCounter: IUnknown;
  129.   StringsFileSystemIndex: Integer;
  130.  
  131. procedure Register;
  132. procedure Unregister;
  133.  
  134. implementation
  135.  
  136. uses Forms, DsgnIntf, LibIntf, DesignConst;
  137.  
  138. type
  139.   TComponentHack = class(TComponent);
  140.  
  141. procedure SplitComponentName(const Ident: string; var Idents: array of string);
  142. var
  143.   ID: string;
  144.   DotPos: Integer;
  145.   I: Integer;
  146. begin
  147.   ID := Ident;
  148.   for I := Low(Idents) to High(Idents) do
  149.   begin
  150.     DotPos := Pos(DotSep, ID);
  151.     if DotPos > 0 then
  152.       Idents[I] := Copy(ID, 1, DotPos - 1)
  153.     else Idents[I] := ID;
  154.     if I < High(IDents) then Delete(ID, 1, DotPos + Length(DotSep) - 1);
  155.   end;
  156. end;
  157.  
  158. procedure FindFormAndComponent(const IDent: string;
  159.   var Component: TComponent; var APropName: string);
  160. var
  161.   NameComponents: array[0..2] of string;
  162.   LibForm: TIForm;
  163. begin
  164.   Component := nil;
  165.   SplitComponentName(Ident, NameComponents);
  166.   LibForm := CompLib.FindForm(NameComponents[0]);
  167.   if LibForm = nil then
  168.   begin
  169.     DelphiIDE.OpenForm(NameComponents[0], False);
  170.     LibForm := CompLib.FindForm(NameComponents[0]);
  171.   end;
  172.   if LibForm <> nil then
  173.     Component := LibForm.Designer.GetRoot.FindComponent(NameComponents[1]);
  174.   if Component <> nil then APropName := NameComponents[2];
  175. end;
  176.  
  177. { TOTANotifier }
  178.  
  179. procedure TOTANotifier.AfterSave;
  180. begin
  181.   {}
  182. end;
  183.  
  184. procedure TOTANotifier.BeforeSave;
  185. begin
  186.   {}
  187. end;
  188.  
  189. constructor TOTANotifier.Create(AOwner: TActiveTStringsProperty);
  190. begin
  191.   inherited Create;
  192.   FOwner := AOWner;
  193. end;
  194.  
  195. procedure TOTANotifier.Destroyed;
  196. begin
  197.   FOwner.Free;
  198. end;
  199.  
  200. procedure TOTANotifier.Modified;
  201. begin
  202.   {}
  203. end;
  204.  
  205. { TOTAModuleNotifier }
  206.  
  207. function TOTAModuleNotifier.CheckOverwrite: Boolean;
  208. begin
  209.   Result := True;
  210. end;
  211.  
  212. procedure TOTAModuleNotifier.Modified;
  213. begin
  214.   if FOwner.FFormEditor <> nil then FOwner.FFormEditor.MarkModified;
  215. end;
  216.  
  217. procedure TOTAModuleNotifier.ModuleRenamed(const NewName: string);
  218. begin
  219.   if AnsiCompareFileName(StringsFileSystem.FActiveTStringsProperties[FOwner.FIndex],
  220.     FOwner.FModule.FileName) <> 0 then
  221.   begin
  222.     FOwner.Module.FileSystem := ''; {Reset the file system to the default}
  223.     FOwner.Free;
  224.   end;
  225. end;
  226.  
  227. { TOTAFormNotifier }
  228.  
  229. procedure TOTAFormNotifier.ComponentRenamed(ComponentHandle: TOTAHandle;
  230.   const OldName, NewName: string);
  231. begin
  232.   FOwner.RenameComponent(OldName, NewName);
  233. end;
  234.  
  235. procedure TOTAFormNotifier.FormActivated;
  236. begin
  237. end;
  238.  
  239. procedure TOTAFormNotifier.FormSaving;
  240. begin
  241.   FOwner.FModule.Save(False, False);
  242. end;
  243.  
  244. { TActiveTStringsProperty }
  245.  
  246. constructor TActiveTStringsProperty.CreateNew(AOwner, AComponent: TComponent;
  247.   const APropertyName: string);
  248. begin
  249.   inherited Create(AOwner);
  250.   FModuleNotifier := TOTAModuleNotifier.Create(Self);
  251.   FFormModuleNotifier := TOTAFormNotifier.Create(Self);
  252.   SetComponent(AComponent);
  253.   FPropertyName := APropertyName;
  254. end;
  255.  
  256. destructor TActiveTStringsProperty.Destroy;
  257. begin
  258.   with StringsFileSystem.FActiveTStringsProperties do
  259.     Delete(IndexOfObject(Self));
  260.   SetModule(nil);
  261.   if FFormEditor <> nil then
  262.     FFormEditor.RemoveNotifier(FFormNotifierIndex);
  263.   inherited Destroy;
  264. end;
  265.  
  266. procedure TActiveTStringsProperty.Notification(AComponent: TComponent;
  267.   Operation: TOperation);
  268. begin
  269.   inherited Notification(AComponent, Operation);
  270.   if (AComponent = FComponent) and (Operation = opRemove) and
  271.     (FModule <> nil) then FModule.CloseModule(True);
  272. end;
  273.  
  274. procedure TActiveTStringsProperty.RenameComponent(const OldName, NewName: string);
  275. var
  276.   NewModuleName: string;
  277. begin
  278.   if CompareText(FComponent.Name, OldName) = 0 then
  279.     NewModuleName := Format('%s.%s.%s', [FComponent.Owner.Name, NewName,
  280.       FPropertyName])
  281.   else if CompareText(FComponent.Owner.Name, OldName) = 0 then
  282.     NewModuleName := Format('%s.%s.%s', [NewName, FComponent.Name,
  283.       FPropertyName]);
  284.   if NewModuleName <> '' then
  285.   begin
  286.     StringsFileSystem.FActiveTStringsProperties[FIndex] := NewModuleName;
  287.     FModule.SetFileName(NewModuleName);
  288.   end;
  289. end;
  290.  
  291. procedure TActiveTStringsProperty.SetModule(const Value: IOTAModule);
  292. begin
  293.   if FModule <> nil then
  294.   begin
  295.     if FModuleNotifier <> nil then
  296.       FModule.RemoveNotifier(FModuleNotifierIndex);
  297.     FModule := nil;
  298.   end;
  299.   FModule := Value;
  300.   if (FModule<> nil) and (FModuleNotifier <> nil) then
  301.     FModuleNotifierIndex := FModule.AddNotifier(FModuleNotifier);
  302. end;
  303.  
  304. procedure TActiveTStringsProperty.SetComponent(Value: TComponent);
  305. var
  306.   ModuleServices: IOTAModuleServices;
  307.   FormModule: IOTAModule;
  308. begin
  309.   if Value <> FComponent then
  310.   begin
  311.     if FComponent <> nil then
  312.     begin
  313.       Notification(FComponent, opRemove);
  314.       TComponentHack(FComponent).Notification(Self, opRemove);
  315.       if FFormEditor <> nil then
  316.         FFormEditor.RemoveNotifier(FFormNotifierIndex);
  317.       FFormEditor := nil;
  318.     end;
  319.     FComponent := Value;
  320.     if FComponent <> nil then
  321.     begin
  322.       FComponent.FreeNotification(Self);
  323.       ModuleServices := BorlandIDEServices as IOTAModuleServices;
  324.       FormModule := ModuleServices.FindFormModule(FComponent.Owner.Name);
  325.       Assert(FormModule <> nil, 'Cannot Locate Form Module!'); //Do not localize
  326.       Assert(FormModule.GetModuleFileCount > 1, 'Not a Form Module'); //Do not localize
  327.       FFormEditor := FormModule.GetModuleFileEditor(1) as IOTAFormEditor;
  328.       FFormNotifierIndex := FFormEditor.AddNotifier(FFormModuleNotifier);
  329.     end;
  330.   end;
  331. end;
  332.  
  333. { TStringsStream }
  334.  
  335. constructor TStringsStream.Create(ActiveTStringsProperty: TActiveTStringsProperty;
  336.   AComponent: TComponent; const APropName: string; Mode: Integer);
  337. var
  338.   PropInfo: PPropInfo;
  339. begin
  340.   inherited Create(nil, soOwned);  // creates owned memory stream for us
  341.   FComponent := AComponent;
  342.   FActiveTStringsProperty := ActiveTStringsProperty;
  343.   SetModifyTime(FActiveTStringsProperty.DiskAge);
  344.   PropInfo := GetPropInfo(AComponent.ClassInfo, APropName);
  345.   if PropInfo = nil then
  346.     raise Exception.CreateResFmt(@sCantFindProperty, [APropName, AComponent.Name]);
  347.   FStringsProperty := TStrings(GetOrdProp(AComponent, PropInfo));
  348.   if FStringsProperty = nil then
  349.     raise Exception.CreateResFmt(@sStringsPropertyInvalid, [APropName,
  350.       AComponent.Name]);
  351.   if Mode <> fmCreate then FStringsProperty.SaveToStream(MemoryStream);
  352.   MemoryStream.Position := 0;
  353. end;
  354.  
  355. destructor TStringsStream.Destroy;
  356. begin
  357.   if FModified then
  358.   begin
  359.     MemoryStream.Position := 0;
  360.     FStringsProperty.LoadFromStream(MemoryStream);
  361.     FActiveTStringsProperty.DiskAge := GetModifyTime;
  362.     SetDesignerModified;
  363.   end;
  364.   inherited Destroy;
  365. end;
  366.  
  367. function TStringsStream.Write(pv: Pointer; cb: Longint; pcbWritten: PLongint): HResult;
  368. begin
  369.   Result := inherited Write(pv, cb, pcbWritten);
  370.   if (cb > 0) and (Result = 0) then FModified := True;
  371. end;
  372.  
  373. procedure TStringsStream.SetDesignerModified;
  374. var
  375.   OwnerComponent: TComponent;
  376. begin
  377.   OwnerComponent := FComponent.Owner;
  378.   while (OwnerComponent <> nil) and not (OwnerComponent is TCustomForm) do
  379.     OwnerComponent := OwnerComponent.Owner;
  380.   if (OwnerComponent <> nil) and (TForm(OwnerComponent).Designer <> nil) then
  381.     TForm(OwnerComponent).Designer.Modified;
  382. end;
  383.  
  384. { TStringsFileSystem }
  385.  
  386. constructor TStringsFileSystem.Create;
  387. begin
  388.   inherited Create;
  389.   FActiveTStringsProperties := TStringList.Create;
  390. end;
  391.  
  392. destructor TStringsFileSystem.Destroy;
  393. begin
  394.   FActiveTStringsProperties.Free;
  395.   inherited Destroy;
  396. end;
  397.  
  398. function TStringsFileSystem.GetTStringsProperty(const Ident: string;
  399.   AComponent: TComponent; const APropertyName: string): TActiveTStringsProperty;
  400. var
  401.   Index: Integer;
  402. begin
  403.   Index := FActiveTStringsProperties.IndexOf(Ident);
  404.   if Index > -1 then
  405.     TObject(Result) := FActiveTStringsProperties.Objects[Index]
  406.   else
  407.   begin
  408.     Result := TActiveTStringsProperty.CreateNew(Application, AComponent,
  409.       APropertyName);
  410.     Result.Module := (BorlandIDEServices as IOTAModuleServices).FindModule(Ident);
  411.     Result.FIndex := FActiveTStringsProperties.AddObject(Ident, Result);
  412.   end;
  413. end;
  414.  
  415. function TStringsFileSystem.GetFileStream(const FileName: string; Mode: Integer): IStream;
  416. var
  417.   Component: TComponent;
  418.   PropertyName: string;
  419.  
  420.   procedure Error;
  421.   begin
  422.     raise Exception.CreateResFmt(@sUnableToFindComponent, [FileName]);
  423.   end;
  424.  
  425. begin
  426.   Result := nil;
  427.   FindFormAndComponent(FileName, Component, PropertyName);
  428.   if Component <> nil then
  429.     Result := TStringsStream.Create(GetTStringsProperty(FileName, Component,
  430.       PropertyName), Component, PropertyName, Mode)
  431.   else Error;
  432. end;
  433.  
  434. function TStringsFileSystem.FileAge(const FileName: string): Longint;
  435. var
  436.   ActiveTStringsProperty: TActiveTStringsProperty;
  437.   Index: Integer;
  438. begin
  439.   Index := FActiveTStringsProperties.IndexOf(FileName);
  440.   if Index > -1 then
  441.   begin
  442.     TObject(ActiveTStringsProperty) := FActiveTStringsProperties.Objects[Index];
  443.     Result := ActiveTStringsProperty.DiskAge;
  444.   end else Result := -1;
  445. end;
  446.  
  447. function TStringsFileSystem.RenameFile(const OldName, NewName: string): Boolean;
  448. begin
  449.   Result := True;
  450. end;
  451.  
  452. function TStringsFileSystem.IsReadonly(const FileName: string): Boolean;
  453. begin
  454.   Result := False;
  455. end;
  456.  
  457. function TStringsFileSystem.IsFileBased: Boolean;
  458. begin
  459.   Result := False;
  460. end;
  461.  
  462. function TStringsFileSystem.DeleteFile(const FileName: string): Boolean;
  463. begin
  464.   Result := True;
  465. end;
  466.  
  467. function TStringsFileSystem.FileExists(const FileName: string): Boolean;
  468. begin
  469.   Result := FActiveTStringsProperties.IndexOf(FileName) <> -1;
  470. end;
  471.  
  472. function TStringsFileSystem.GetTempFileName(const FileName: string): string;
  473. begin
  474.   Result := FileName;
  475. end;
  476.  
  477. function TStringsFileSystem.GetBackupFileName(const FileName: string): string;
  478. begin
  479.   Result := FileName;
  480. end;
  481.  
  482. function TStringsFileSystem.GetIDString: string;
  483. begin
  484.   Result := sTStringsFileSystem;
  485. end;
  486.  
  487. procedure Register;
  488. var
  489.   ModuleServices: IOTAModuleServices;
  490. begin
  491.   if (StringsFileSystem = nil) and
  492.     Succeeded(BorlandIDEServices.QueryInterface(IOTAModuleServices, ModuleServices)) then
  493.   begin
  494.     StringsFileSystem := TStringsFileSystem.Create;
  495.     StringsFileSystemCounter := StringsFileSystem;
  496.     StringsFileSystemIndex := ModuleServices.AddFileSystem(StringsFileSystem);
  497.     if StringsFileSystemIndex < 0 then
  498.     begin
  499.       StringsFileSystem := nil;
  500.       StringsFileSystemCounter := nil;
  501.     end;
  502.   end;
  503. end;
  504.  
  505. procedure Unregister;
  506. var
  507.   ModuleServices: IOTAModuleServices;
  508. begin
  509.   if (StringsFileSystemIndex > -1) and
  510.     Succeeded(BorlandIDEServices.QueryInterface(IOTAModuleServices, ModuleServices)) then
  511.   begin
  512.     StringsFileSystem := nil;
  513.     StringsFileSystemCounter := nil;
  514.     ModuleServices.RemoveFileSystem(StringsFileSystemIndex);
  515.   end;
  516. end;
  517.  
  518. end.
  519.